// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef FBL_UNIQUE_FREE_PTR_H_
#define FBL_UNIQUE_FREE_PTR_H_

#include <stdlib.h>
#include <fbl/macros.h>

namespace fbl {

// This is exactly like unique_ptr except that it deallocates using free() instead of delete.
// This should only be used on types without destructors, like 'char' or C structs.
template <typename T>
class unique_free_ptr {
 public:
  static_assert(std::is_trivially_destructible_v<T>,
                "unique_free_ptr can only be used on types with trivial destructors");

  constexpr unique_free_ptr() : ptr_(nullptr) {}
  constexpr unique_free_ptr(decltype(nullptr)) : unique_free_ptr() {}

  explicit unique_free_ptr(T* t) : ptr_(t) {}

  ~unique_free_ptr() { free(ptr_); }

  unique_free_ptr(unique_free_ptr&& o) : ptr_(o.release()) {}
  unique_free_ptr& operator=(unique_free_ptr&& o) {
    reset(o.release());
    return *this;
  }

  unique_free_ptr& operator=(decltype(nullptr)) {
    reset();
    return *this;
  }

  // Comparison against nullptr operators (of the form, myptr == nullptr).
  bool operator==(decltype(nullptr)) const { return (ptr_ == nullptr); }
  bool operator!=(decltype(nullptr)) const { return (ptr_ != nullptr); }

  // Comparison against other unique_free_ptr<>'s.
  bool operator==(const unique_free_ptr& o) const { return ptr_ == o.ptr_; }
  bool operator!=(const unique_free_ptr& o) const { return ptr_ != o.ptr_; }
  bool operator<(const unique_free_ptr& o) const { return ptr_ < o.ptr_; }
  bool operator<=(const unique_free_ptr& o) const { return ptr_ <= o.ptr_; }
  bool operator>(const unique_free_ptr& o) const { return ptr_ > o.ptr_; }
  bool operator>=(const unique_free_ptr& o) const { return ptr_ >= o.ptr_; }

  // move semantics only
  DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(unique_free_ptr);

  T* release() {
    T* t = ptr_;
    ptr_ = nullptr;
    return t;
  }
  void reset(T* t = nullptr) {
    free(ptr_);
    ptr_ = t;
  }
  void swap(unique_free_ptr& other) {
    T* t = ptr_;
    ptr_ = other.ptr_;
    other.ptr_ = t;
  }

  T* get() const { return ptr_; }

  explicit operator bool() const { return static_cast<bool>(ptr_); }

  T& operator*() const { return *ptr_; }
  T* operator->() const { return ptr_; }

 private:
  T* ptr_;
};

// Comparison against nullptrs (of the form, nullptr == myptr)
template <typename T>
static inline bool operator==(decltype(nullptr), const unique_free_ptr<T>& ptr) {
  return (ptr.get() == nullptr);
}

template <typename T>
static inline bool operator!=(decltype(nullptr), const unique_free_ptr<T>& ptr) {
  return (ptr.get() != nullptr);
}

}  // namespace fbl

#endif  // FBL_UNIQUE_FREE_PTR_H_
